package com.chen.base.config;

import com.chen.root.base.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.PropertyAccessException;
import org.springframework.core.annotation.Order;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.util.NestedServletException;
import springfox.documentation.annotations.ApiIgnore;

import java.util.List;

/**
 * <p>
 * 全局异常处理器结合sentinel 全局异常处理器不能作用在 oauth server https://gitee.com/log4j/pig/issues/I1M2TJ
 * </p>
 *
 * @author long
 * @date 2023-05-14
 */
@Slf4j
@Order
@ApiIgnore
@RestController
@RestControllerAdvice
public class GlobalExceptionHandler {

	/**
	 * 全局异常
	 */
	@ExceptionHandler(Exception.class)
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	public R<String> handleGlobalException(Exception e) {
		log.error("全局异常信息 ↓", e);
		return R.failed(null, e.getMessage());
	}

	/**
	 * 参数绑定异常
	 */
	@ExceptionHandler(NestedServletException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public R<String> handleNestedServletException(NestedServletException e) {
		log.error("参数绑定异常, ex = {}", e.getMessage());
		return R.failed(null, String.format("参数绑定异常, ex = %s", e.getMessage()));
	}

	/**
	 * 参数不合法
	 */
	@ExceptionHandler({ConversionFailedException.class})
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public R<String> handleConversionFailedException(ConversionFailedException e) {
		log.error("参数解析失败, ex = {}", e.getLocalizedMessage());
		return R.failed(null, String.format("参数解析失败, ex = %s", e.getMessage()));
	}

	/**
	 * validation Exception
	 */
	@ExceptionHandler({MethodArgumentNotValidException.class})
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public R<String> handleBodyValidException(MethodArgumentNotValidException e) {
		List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
		log.error("参数绑定异常, ex = {}", fieldErrors.get(0).getDefaultMessage());
		return R.failed(null, fieldErrors.get(0).getDefaultMessage());
	}

	/**
	 * validation Exception
	 */
	@ExceptionHandler({HttpMessageNotReadableException.class})
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public R<String> handleHttpMessageNotReadableException(HttpMessageNotReadableException e) {
		log.error("参数绑定异常, ex = {}", e.getMessage());
		return R.failed(null, String.format("参数绑定异常, ex = %s", e.getMessage()));
	}

	/**
	 * 原有的统一异常处理不能满足要求，追加对BindException的处理
	 * 处理 form data方式调用接口校验失败抛出的异常
	 */
	@ExceptionHandler({BindException.class})
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public R<String> handleFormDataBindException(BindException e) {
		List<FieldError> fieldErrors = e.getBindingResult().getFieldErrors();
		StringBuilder sb = new StringBuilder();
		for (FieldError fieldError : fieldErrors) {
			fieldError.getField();
			sb.append(fieldError.getField()).append(" ").append(fieldError.getDefaultMessage()).append(";");
			log.error("参数绑定异常,params = {}, ex = {}", fieldError.getField(), fieldError.getDefaultMessage());
		}

		return R.failed(null, sb.toString());
	}

	/**
	 * 针对非法入参的异常拦截处理
	 */
	@ExceptionHandler({IllegalArgumentException.class})
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public R<String> handleIllegalArgumentException(IllegalArgumentException e) {
		log.error("非法参数错误, ex = {}", e.getLocalizedMessage());
		return R.failed(null, String.format("非法参数错误, ex = %s", e.getLocalizedMessage()));
	}

	/**
	 * 针对非法入参的异常拦截处理
	 */
	@ExceptionHandler({PropertyAccessException.class})
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public R<String> handlePropertyAccessException(PropertyAccessException e) {
		log.error("非法参数错误, ex = {}", e.getLocalizedMessage());
		return R.failed(null, String.format("非法参数错误, ex = %s", e.getLocalizedMessage()));
	}

	/**
	 * 避免 404 重定向到 /error 导致NPE ,ignore-url 需要配置对应端点
	 */
	@DeleteMapping("/error")
	@ResponseStatus(HttpStatus.NOT_FOUND)
	public R<String> noHandlerFoundException() {
		return R.failed(null, HttpStatus.NOT_FOUND.getReasonPhrase());
	}
}
